/* packet-ipx.c
 * Routines for NetWare's IPX
 * Gilbert Ramirez <gram@verdict.uthscsa.edu>
 *
 * Ethereal - Network traffic analyzer
 * By Gerald Combs <gerald@unicom.net>
 * Copyright 1998 Gerald Combs
 *
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

#include "config.h"

#include <gtk/gtk.h>
#include <pcap.h>

#include <stdio.h>

#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif

#ifdef _AIX
#include <netinet/in.h>
#endif

#include "ethereal.h"
#include "packet.h"

struct port_info {
	u_short	port;
	char	*text;
};

static char*
port_text(u_short port) {
	int i=0;

	static struct port_info	ports[] = {
		{ 0x0451, "NCP" },
		{ 0x0452, "SAP" },
		{ 0x0453, "RIP" },
		{ 0x0455, "NetBIOS" },
		{ 0x0456, "Diagnostic" },
		{ 0x0457, "Serialization" },
		{ 0x0000, NULL }
	};

	while (ports[i].text != NULL) {
		if (ports[i].port == port) {
			return ports[i].text;
		}
		i++;
	}
	return "Unknown";
}

char *
ipx_packet_type(u_char val)
{
	if (val == 0) {
		return "IPX";
	}
	else if (val == 5) {
		return "SPX";
	}
	else if (val == 17) {
		return "NCP";
	}
	else if (val == 20) {
		return "NetBIOS";
	}
	else if (val >= 16 && val <= 31) {
		return "Experimental Protocol";
	}
	else {
		return "Unknown";
	}
}

gchar*
network_to_string(const guint8 *ad)
{
	static gchar	str[3][12];
	static gchar	*cur;

	if (cur == &str[0][0]) {
		cur = &str[1][0];
	} else if (cur == &str[1][0]) {
		cur = &str[2][0];
	} else {
		cur = &str[0][0];
	}

	sprintf(cur, "%02X %02X %02X %02X", ad[0], ad[1], ad[2], ad[3]);
	return cur;
}

void
dissect_ipx(const u_char *pd, int offset, frame_data *fd, GtkTree *tree) {

	GtkWidget	*ipx_tree, *ti;
	u_char		ipx_type;

	char		*dnet, *snet;
	guint16		dsocket, ssocket;

	/* Calculate here for use in win_info[] and in tree */
	dnet = network_to_string((guint8*)&pd[offset+6]);
	snet = network_to_string((guint8*)&pd[offset+18]);

	if (fd->win_info[0]) {
		strcpy(fd->win_info[3], "IPX");
		sprintf(fd->win_info[4], "Network %s --> %s", snet, dnet);
	}

	ipx_type = pd[offset+5];

	if (tree) {
		ti = add_item_to_tree(GTK_WIDGET(tree), offset, 30,
			"Internetwork Packet Exchange");
		ipx_tree = gtk_tree_new();
		gtk_tree_item_set_subtree(GTK_TREE_ITEM(ti), ipx_tree);
		add_item_to_tree(ipx_tree, offset,      2, "Checksum: 0x%04X",
			(pd[offset] << 8) | pd[offset+1]);
		add_item_to_tree(ipx_tree, offset+2,    2, "Length: %d bytes",
			(pd[offset+2] << 8) | pd[offset+3]);
		add_item_to_tree(ipx_tree, offset+4,    1, "Transport Control: %d hops",
			pd[offset+4]);
		add_item_to_tree(ipx_tree, offset+5,    1, "Packet Type: %s",
			ipx_packet_type(ipx_type));
		add_item_to_tree(ipx_tree, offset+6,    4, "Destination Network: %s",
			dnet);
		add_item_to_tree(ipx_tree, offset+10,   6, "Destination Node: %s",
			ether_to_str((guint8*)&pd[offset+10]));
		dsocket = ntohs(*((guint16*)&pd[offset+16]));
		add_item_to_tree(ipx_tree, offset+16,   2,
			"Destination Socket: %s (0x%04X)", port_text(dsocket), dsocket);
		add_item_to_tree(ipx_tree, offset+18,   4, "Source Network: %s",
			snet);
		add_item_to_tree(ipx_tree, offset+22,   6, "Source Node: %s",
			ether_to_str((guint8*)&pd[offset+22]));
		ssocket = ntohs(*((guint16*)&pd[offset+28]));
		add_item_to_tree(ipx_tree, offset+28,   2,
			"Source Socket: %s (0x%04X)", port_text(ssocket), ssocket);
	}
	offset += 30;

	switch (ipx_type) {
		case 0: /* IPX */
			/* I'm the IPX layer!!! */
			break;
		case 5: /* SPX */
			dissect_raw(pd, offset, fd, tree); /* until implemented */
			break;
		case 17: /* NCP */
			dissect_raw(pd, offset, fd, tree); /* until implemented */
			break;
		case 20: /* NetBIOS */
			dissect_raw(pd, offset, fd, tree); /* until implemented */
			break;
		default:
			dissect_raw(pd, offset, fd, tree);
			break;
	}
}
